﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using Common.Utilities;

namespace Workflows.Components.TextMining.Clustering
{
    /// <summary>
    /// 
    /// </summary>
    public partial class ExportClusterResultToExcelFile : Form
    {
        private string _DocTermListFilePath = string.Empty;
        private string _ClusterResultFilePath = string.Empty;

        #region network diagram navigation
        private Dictionary<int, NetworkDiagram.Node> _Nodes;
        private Dictionary<int, NetworkDiagram.Leaf> _Leaves;
        private Dictionary<int, NetworkDiagram.Link> _NodeLinks;
        private Dictionary<int, Dictionary<int, NetworkDiagram.Link>> _LeafLinks;
        private Dictionary<int, Point> _NetworkClusterNodePositions;
        private Dictionary<int, Point> _NetworkDocNodePositions;
        #endregion
        /// <summary>
        /// 
        /// </summary>
        /// <param name="docTermListFilePath"></param>
        /// <param name="clusterResultFilePath"></param>
        public ExportClusterResultToExcelFile(
            string docTermListFilePath,
            string clusterResultFilePath)
        {
            this._DocTermListFilePath = docTermListFilePath;
            this._ClusterResultFilePath = clusterResultFilePath;
            InitializeComponent();
        }

        private void ExportClusterResultToExcelFile_Load(object sender, EventArgs e)
        {
            if(!string.IsNullOrEmpty(this._ClusterResultFilePath) && File.Exists(this._ClusterResultFilePath))
            {
                XmlDocument xDoc = new XmlDocument();
                xDoc.Load(this._ClusterResultFilePath);
                this._Nodes = new Dictionary<int, NetworkDiagram.Node>();
                this._Leaves = new Dictionary<int, NetworkDiagram.Leaf>();
                this._NodeLinks = new Dictionary<int, NetworkDiagram.Link>();
                this._LeafLinks = new Dictionary<int, Dictionary<int, NetworkDiagram.Link>>();

                XmlNodeList clusterNodes = xDoc.SelectNodes("Clusters/Cluster");
                foreach (XmlNode clusterNode in clusterNodes)
                {
                    int nodeID = int.Parse(clusterNode.Attributes.GetNamedItem("ClusterID").Value);
                    NetworkDiagram.Node node = new NetworkDiagram.Node();
                    node.Label = nodeID.ToString();
                    node.NodeID = nodeID;
                    this._Nodes.Add(nodeID, node);

                    XmlNodeList docNodes = clusterNode.SelectNodes("Docs/Doc");
                    if (docNodes != null && docNodes.Count > 0)
                    {
                        foreach (XmlNode docNode in docNodes)
                        {
                            int leafID = int.Parse(docNode.Attributes.GetNamedItem("DocID").Value);
							string label = docNode.Attributes.GetNamedItem("Label").Value;
                            NetworkDiagram.Leaf leaf = new NetworkDiagram.Leaf();
                            leaf.LeafID = leafID;
                            leaf.Label = label;
                            if (!this._Leaves.ContainsKey(leafID))
                            {
                                this._Leaves.Add(leafID, leaf);
                            }
                            if (!this._LeafLinks.ContainsKey(leafID))
                            {
                                NetworkDiagram.Link link = new NetworkDiagram.Link();
                                link.Distance = 1; // double.Parse(docNode.Attributes.GetNamedItem("Distance").Value);
                                link.FromID = leafID;
                                link.FromVerticeIsNode = false;
                                link.ToID = nodeID;
                                Dictionary<int, NetworkDiagram.Link> links = new Dictionary<int, NetworkDiagram.Link>();
                                links.Add(nodeID, link);
                                this._LeafLinks.Add(leafID, links);
                            }
                            else
                            {
                                Dictionary<int, NetworkDiagram.Link> links = this._LeafLinks[leafID];
                                NetworkDiagram.Link link = new NetworkDiagram.Link();
                                link.Distance = 1;  // double.Parse(docNode.Attributes.GetNamedItem("Distance").Value);
                                link.FromID = leafID;
                                link.FromVerticeIsNode = false;
                                link.ToID = nodeID;
                                if (!links.ContainsKey(nodeID))
                                {
                                    links.Add(nodeID, link);
                                }
                                this._LeafLinks[leafID] = links;
                            }
                        }
                    }

                    XmlNodeList childClusterNodes = clusterNode.SelectNodes("ChildClusters/Cluster");
                    if (childClusterNodes != null && childClusterNodes.Count > 0)
                    {
                        foreach (XmlNode childClusterNode in childClusterNodes)
                        {
                            int childClusterID = int.Parse(childClusterNode.Attributes.GetNamedItem("ClusterID").Value);
                            NetworkDiagram.Link link = new NetworkDiagram.Link();
                            link.Distance = 1;
                            link.FromID = childClusterID;
                            link.FromVerticeIsNode = true;
                            link.ToID = nodeID;
                            this._NodeLinks.Add(childClusterID, link);
                        }
                    }
                }

                // tree view
                this.PopulateClusterToTreeView(this._Nodes, this._Leaves, this._NodeLinks, this._LeafLinks);
            }
        }

        private void PopulateClusterToTreeView(
            Dictionary<int, NetworkDiagram.Node> nodes,
            Dictionary<int, NetworkDiagram.Leaf> leaves,
            Dictionary<int, NetworkDiagram.Link> nodeLinks,
            Dictionary<int, Dictionary<int, NetworkDiagram.Link>> leafLinks)
        {
            this.tree_Clusters.Nodes.Clear();
            Dictionary<int, TreeNode> nodeIDTreeNodeMappings = new Dictionary<int, TreeNode>();

            List<int> rootNodeIDs = new List<int>();
            foreach (int nodeID in nodes.Keys)
            {
                if (nodeLinks.ContainsKey(nodeID))
                {
                    NetworkDiagram.Link nodeLink = nodeLinks[nodeID];
                    if (nodeLink.ToID <= 0)
                    {
                        rootNodeIDs.Add(nodeID);
                    }
                }
                else
                {
                    rootNodeIDs.Add(nodeID);
                }
            }

            foreach (int nodeID in rootNodeIDs)
            {
                TreeNode clusterTn = new TreeNode(nodes[nodeID].Label);
                clusterTn.ImageIndex = 0;
                clusterTn.SelectedImageIndex = 0;
                clusterTn.Tag = nodeID;
                this.tree_Clusters.Nodes.Add(clusterTn);
                nodeIDTreeNodeMappings.Add(nodeID, clusterTn);

                this.AddChildClusters(ref nodeIDTreeNodeMappings, clusterTn,
                    nodeID, nodes, nodeLinks, leaves, leafLinks);
                this.AddChildDocsToTreeNode(clusterTn, nodeID, leaves, leafLinks);
            }
        }

        private void AddChildDocsToTreeNode(
            TreeNode parentTn, int parentClusterID,
            Dictionary<int, NetworkDiagram.Leaf> leaves,
            Dictionary<int, Dictionary<int, NetworkDiagram.Link>> leafLinks)
        {
            foreach (int leafID in leafLinks.Keys)
            {
                Dictionary<int, NetworkDiagram.Link> links = leafLinks[leafID];
                if (links.ContainsKey(parentClusterID))
                {
                    TreeNode docTn = new TreeNode(leaves[leafID].Label);
                    docTn.ImageIndex = 1;
                    docTn.SelectedImageIndex = 1;
                    docTn.Tag = leafID;
                    parentTn.Nodes.Add(docTn);
                }
            }
        }

        private void AddChildClusters(
            ref Dictionary<int, TreeNode> clusterIDTreeNodeMappings,
            TreeNode parentTn, int parentClusterID,
            Dictionary<int, NetworkDiagram.Node> nodes,
            Dictionary<int, NetworkDiagram.Link> nodeLinks,
            Dictionary<int, NetworkDiagram.Leaf> leaves,
            Dictionary<int, Dictionary<int, NetworkDiagram.Link>> leafLinks)
        {
            foreach (int nodeID in nodeLinks.Keys)
            {
                NetworkDiagram.Link link = nodeLinks[nodeID];
                if (link.ToID == parentClusterID)
                {
                    TreeNode childClusterTn = new TreeNode(nodes[nodeID].Label);
                    childClusterTn.ImageIndex = 0;
                    childClusterTn.SelectedImageIndex = 0;
                    childClusterTn.Tag = nodeID;
                    parentTn.Nodes.Add(childClusterTn);
                    clusterIDTreeNodeMappings.Add(nodeID, childClusterTn);

                    this.AddChildClusters(ref clusterIDTreeNodeMappings,
                                          childClusterTn, nodeID,
                                          nodes, nodeLinks, leaves, leafLinks);
                    this.AddChildDocsToTreeNode(childClusterTn, nodeID, leaves, leafLinks);
                }
            }
        }

        private void tree_Clusters_MouseUp(object sender, MouseEventArgs e)
        {
            this.tree_Clusters.SelectedNode = this.tree_Clusters.GetNodeAt(e.X, e.Y);
            if (this.tree_Clusters.SelectedNode != null)
            {
                if (this.tree_Clusters.SelectedNode.ImageIndex == 0)
                {
                    int clusterID = (int)this.tree_Clusters.SelectedNode.Tag;
                    this.ShowCluster(clusterID);
                }
                else if (this.tree_Clusters.SelectedNode.ImageIndex == 1)
                {
                    int docID = (int)this.tree_Clusters.SelectedNode.Tag;
                    this.ShowDoc(docID);
                }
            }
        }

        private void ShowCluster(int clusterID)
        {
            this.rt_Content.Text = string.Empty;
            if (!string.IsNullOrEmpty(this._DocTermListFilePath))
            {
                XmlDocument xDoc = new XmlDocument();
                xDoc.Load(this._ClusterResultFilePath);
                XmlNode clusterNode = xDoc.SelectSingleNode("Clusters/Cluster[@ClusterID=\"" +
                    clusterID + "\"]");
                if (clusterNode != null)
                {
                    StringBuilder buffer = new StringBuilder();
                    XmlNodeList sharedTermNodes = clusterNode.SelectNodes("SharedTerms/Term");
                    if (sharedTermNodes != null && sharedTermNodes.Count > 0)
                    {
                        foreach (XmlNode termNode in sharedTermNodes)
                        {
                            int termID = int.Parse(termNode.Attributes.GetNamedItem("TermID").Value);
							string phrase = termNode.Attributes.GetNamedItem("Term").Value;
                            if (buffer.Length > 0)
                            {
                                buffer.Append(", ");
                            }
                            buffer.Append(phrase);
                        }
                    }
                    this.rt_Content.Text = buffer.ToString();
                }
            }
        }

        private void ShowDoc(int docID)
        {
            this.rt_Content.Text = string.Empty;
            if (!string.IsNullOrEmpty(this._DocTermListFilePath))
            {
                XmlDocument xDoc = new XmlDocument();
                xDoc.Load(this._DocTermListFilePath);
                XmlNode docNode = xDoc.SelectSingleNode("Docs/Doc[@DocID=\"" +
                    docID + "\"]");
                if (docNode != null)
                {
                    string docContent = XmlDataUtil.GetCdataValue(docNode, "");
                    this.rt_Content.Text = docContent;
                }
            }
        }

        private void ck_MakeFlat_CheckedChanged(object sender, EventArgs e)
        {
            if (this.ck_MakeFlat.Checked)
            {
                this.scale_Coverage.Enabled = true;
                this.scale_Distance.Enabled = true;
            }
            else
            {
                this.scale_Coverage.Enabled = false;
                this.scale_Distance.Enabled = false;
            }
        }

        private void btn_UpdateTreeView_Click(object sender, EventArgs e)
        {
            if (this._Nodes != null && this._Nodes.Count > 0 &&
                    this._Leaves != null && this._Leaves.Count > 0 &&
                    this._NodeLinks != null && this._NodeLinks.Count > 0 &&
                    this._LeafLinks != null && this._LeafLinks.Count > 0)
            {
                if (this.ck_MakeFlat.Checked)
                {
                    // root cluster id -> list of doc ids
                    //Dictionary<int, List<int>> flattenedDocClusters = new Dictionary<int, List<int>>();
                    //int depthLimit = (int)this.scale_Coverage.Value;
                    //foreach (TreeNode rootTn in this.tree_Clusters.Nodes)
                    //{
                    //    this.FlatTreeView(ref flattenedDocClusters, rootTn, 0, depthLimit);
                    //}

                    Dictionary<int, List<int>> flattenedDocClusters = this.FlatClusterTree();

                    if (flattenedDocClusters.Count > 0)
                    {
                        this.tree_Clusters.Nodes.Clear();
                        foreach (int clusterID in flattenedDocClusters.Keys)
                        {
                            TreeNode superClusterTn = new TreeNode(this._Nodes[clusterID].Label);
                            superClusterTn.ImageIndex = 0;
                            superClusterTn.SelectedImageIndex = 0;
                            superClusterTn.Tag = clusterID;
                            this.tree_Clusters.Nodes.Add(superClusterTn);

                            List<int> docIDs = flattenedDocClusters[clusterID];
                            foreach (int docID in docIDs)
                            {
                                TreeNode docTn = new TreeNode(this._Leaves[docID].Label);
                                docTn.ImageIndex = 1;
                                docTn.SelectedImageIndex = 1;
                                docTn.Tag = docID;
                                superClusterTn.Nodes.Add(docTn);
                            }
                        }
                    }
                }
                else
                {
                    this.PopulateClusterToTreeView(this._Nodes, this._Leaves, this._NodeLinks, this._LeafLinks);
                }
            }
            else
            {
                MessageBox.Show("Cluster result is not populated, click button \"test\" to make you have data");
            }
        }

        #region flat doc cluster tree
        private Dictionary<int, List<int>> FlatClusterTree()
        {
            double distanceThreshold = (double)this.scale_Distance.Value;
            double coverageThreshold = (double)this.scale_Coverage.Value;

            // cluster id --> list of leaf ids
            Dictionary<int, List<int>> flattenedDocClusters = new Dictionary<int, List<int>>();
            List<NetworkDiagram.Node> seedNodes = new List<NetworkDiagram.Node>();
            Dictionary<int, double> seedNodeDistances = new Dictionary<int, double>();
            Dictionary<int, double> seedNodeCoverages = new Dictionary<int, double>();
            double distanceFromRootToLeaf = this.GetLongestDistanceFromNodeToLeaf(this.GetRoot());

            foreach (NetworkDiagram.Node node in this._Nodes.Values)
            {
                bool containsChildNode = false;
                foreach (NetworkDiagram.Link link in this._NodeLinks.Values)
                {
                    if (link.ToID == node.NodeID && link.FromVerticeIsNode)
                    {
                        containsChildNode = true;
                        break;
                    }
                }
                if (!containsChildNode)
                {
                    seedNodes.Add(node);
                    double distance = 0;
                    double coverage = 0;
                    foreach (int leafID in this._LeafLinks.Keys)
                    {
                        Dictionary<int, NetworkDiagram.Link> toNodeLinks = this._LeafLinks[leafID];
                        if (toNodeLinks.ContainsKey(node.NodeID))
                        {
                            coverage = coverage + 1;
                            distance += toNodeLinks[node.NodeID].Distance;
                        }
                    }
                    if (coverage > 0)
                    {
                        distance = distance / coverage;
                        coverage = coverage / this._Leaves.Count;
                    }
                    seedNodeCoverages.Add(node.NodeID, coverage);
                    seedNodeDistances.Add(node.NodeID, distance);
                }
            }

            int leavesAdded = int.MaxValue;
            while (leavesAdded > 0)
            {
                leavesAdded = 0;
                NetworkDiagram.Node[] seeds = new NetworkDiagram.Node[seedNodes.Count];
                seedNodes.CopyTo(seeds, 0);
                for (int i = 0; i < seeds.Length; i++)
                {
                    NetworkDiagram.Node seed = seeds[i];
                    double distance = seedNodeDistances[seed.NodeID];
                    double coverage = seedNodeCoverages[seed.NodeID];
                    if (distance <= distanceFromRootToLeaf * distanceThreshold &&
                        coverage <= coverageThreshold)
                    {
                        NetworkDiagram.Node parentNode = new NetworkDiagram.Node();
                        foreach (NetworkDiagram.Link nodeLink in this._NodeLinks.Values)
                        {
                            if (nodeLink.FromID == seed.NodeID)
                            {
                                parentNode = this._Nodes[nodeLink.ToID];
                                break;
                            }
                        }
                        if (parentNode.NodeID > 0)
                        {
                            int deltaLeafCount = 0;
                            double deltaDistance = 0;
                            foreach (int leafLinkID in this._LeafLinks.Keys)
                            {
                                Dictionary<int, NetworkDiagram.Link> leafNodeLinks = this._LeafLinks[leafLinkID];
                                if (leafNodeLinks.ContainsKey(parentNode.NodeID))
                                {
                                    deltaLeafCount += 1;
                                }
                            }
                            foreach (NetworkDiagram.Link nodeLink in this._NodeLinks.Values)
                            {
                                if (nodeLink.ToID == parentNode.NodeID && nodeLink.FromID != seed.NodeID)
                                {
                                    NetworkDiagram.Node sibNode = this._Nodes[nodeLink.FromID];
                                    deltaLeafCount += this.GetLeafIDsForNode(sibNode).Count;
                                    deltaDistance += nodeLink.Distance;
                                }
                            }
                            double deltaCoverage = (double)deltaLeafCount / this._Leaves.Count;
                            if (distance + deltaDistance <= distanceFromRootToLeaf * distanceThreshold &&
                                    coverage + deltaCoverage <= coverageThreshold)
                            {
                                if (!seedNodeCoverages.ContainsKey(parentNode.NodeID))
                                {
                                    seedNodeCoverages.Add(parentNode.NodeID, deltaCoverage + coverage);
                                    seedNodeDistances.Add(parentNode.NodeID, deltaDistance + distance);
                                    seedNodes.Add(parentNode);
                                }

                                List<int> childNodeIDs = this.GetChildNodeIDsForNode(parentNode);
                                foreach (int childNodeID in childNodeIDs)
                                {
                                    seedNodeCoverages.Remove(childNodeID);
                                    seedNodeDistances.Remove(childNodeID);
                                    NetworkDiagram.Node childNode = this._Nodes[childNodeID];
                                    seedNodes.Remove(childNode);
                                }
                                leavesAdded += deltaLeafCount;
                                break;
                            }
                        }
                    }
                }
            }

            foreach (NetworkDiagram.Node seed in seedNodes)
            {
                List<int> leafIDs = this.GetLeafIDsForNode(seed);
                flattenedDocClusters.Add(seed.NodeID, leafIDs);
            }

            return flattenedDocClusters;
        }

        private NetworkDiagram.Node GetRoot()
        {
            foreach (NetworkDiagram.Node node in this._Nodes.Values)
            {
                bool containsDownstreamLinks = false;
                foreach (NetworkDiagram.Link link in this._NodeLinks.Values)
                {
                    if (link.FromID == node.NodeID)
                    {
                        containsDownstreamLinks = true;
                        break;
                    }
                }
                //if(!containsDownstreamLinks)
                //{
                //    foreach(int leafLinkID in this._LeafLinks.Keys)
                //    {
                //        Dictionary<int, NetworkDiagram.Link> leafNodeLinks = this._LeafLinks[leafLinkID];
                //        if(leafNodeLinks.ContainsKey(node.NodeID))
                //        {
                //            containsDownstreamLinks = true;
                //            break;
                //        }
                //    }
                //}

                if (!containsDownstreamLinks)
                    return node;
            }

            return new NetworkDiagram.Node();
        }

        private double GetLongestDistanceFromNodeToLeaf(NetworkDiagram.Node fromNode)
        {
            double distance = 0;
            List<NetworkDiagram.Link> toNodeLinks = new List<NetworkDiagram.Link>();

            foreach (NetworkDiagram.Link link in this._NodeLinks.Values)
            {
                if (link.ToID == fromNode.NodeID && link.FromVerticeIsNode)
                {
                    toNodeLinks.Add(link);
                }
            }
            List<NetworkDiagram.Link> toLeafLinks = new List<NetworkDiagram.Link>();
            foreach (int leafLinkID in this._LeafLinks.Keys)
            {
                Dictionary<int, NetworkDiagram.Link> leafToNodeLinks = this._LeafLinks[leafLinkID];
                if (leafToNodeLinks.ContainsKey(fromNode.NodeID))
                {
                    toLeafLinks.Add(leafToNodeLinks[fromNode.NodeID]);
                }
            }
            List<double> leafDistances = new List<double>();
            foreach (NetworkDiagram.Link link in toLeafLinks)
            {
                leafDistances.Add(link.Distance);
            }
            if (leafDistances.Count > 0)
                distance += StatsUtil.GetAverage(leafDistances).Value;
            if (toNodeLinks.Count > 0)
            {
                double maxDownstreamDistance = 0;
                foreach (NetworkDiagram.Link link in toNodeLinks)
                {
                    NetworkDiagram.Node toNode = this._Nodes[link.FromID];
                    double distance1 = link.Distance + this.GetLongestDistanceFromNodeToLeaf(toNode);
                    if (distance1 > maxDownstreamDistance)
                        maxDownstreamDistance = distance1;
                }
                distance += maxDownstreamDistance;
            }
            return distance;
        }

        private List<int> GetLeafIDsForNode(NetworkDiagram.Node node)
        {
            List<int> leafIDs = new List<int>();
            foreach (int leafLinkID in this._LeafLinks.Keys)
            {
                if (this._LeafLinks[leafLinkID].ContainsKey(node.NodeID))
                {
                    int leafID = this._LeafLinks[leafLinkID][node.NodeID].FromID;
                    if (!leafIDs.Contains(leafID))
                        leafIDs.Add(leafID);
                }
            }
            foreach (NetworkDiagram.Link link in this._NodeLinks.Values)
            {
                if (link.ToID == node.NodeID)
                {
                    NetworkDiagram.Node fromNode = this._Nodes[link.FromID];
                    List<int> childLeafIDs = this.GetLeafIDsForNode(fromNode);
                    foreach (int childLeafID in childLeafIDs)
                    {
                        if (!leafIDs.Contains(childLeafID))
                            leafIDs.Add(childLeafID);
                    }
                }
            }

            return leafIDs;
        }

        private List<int> GetChildNodeIDsForNode(NetworkDiagram.Node node)
        {
            List<int> childNodeIDs = new List<int>();
            foreach (NetworkDiagram.Link link in this._NodeLinks.Values)
            {
                if (link.ToID == node.NodeID)
                {
                    NetworkDiagram.Node childNode = this._Nodes[link.FromID];
                    if (!childNodeIDs.Contains(childNode.NodeID))
                        childNodeIDs.Add(childNode.NodeID);
                    List<int> grandChildNodeIDs = this.GetChildNodeIDsForNode(childNode);
                    foreach (int grandChildNodeID in grandChildNodeIDs)
                    {
                        if (!childNodeIDs.Contains(grandChildNodeID))
                            childNodeIDs.Add(grandChildNodeID);
                    }
                }
            }

            return childNodeIDs;
        }
        #endregion

    }
}
